package com.autocareai.lib.list

import java.util.ArrayList

/**
 * <pre>
 *     author : June Yang
 *     time   : 2020/5/7
 *     desc   : 可观察元素变化的数组集合
 *     version: 1.0.0
 * </pre>
 */
class ObservableArrayList<T> : ArrayList<T>() {

    private var onListChangedListener: OnListChangedListener<ObservableArrayList<T>>? = null

    override fun add(element: T): Boolean {
        super.add(element)
        onListChangedListener?.onItemRangeInserted(this, size - 1, 1)
        return true
    }

    override fun add(index: Int, element: T) {
        super.add(index, element)
        onListChangedListener?.onItemRangeInserted(this, index, 1)
    }

    override fun addAll(elements: Collection<T>): Boolean {
        val oldSize = size
        val added = super.addAll(elements)
        if (added) {
            onListChangedListener?.onItemRangeInserted(this, oldSize, size - oldSize)
        }
        return added
    }

    override fun addAll(index: Int, elements: Collection<T>): Boolean {
        val added = super.addAll(index, elements)
        if (added) {
            onListChangedListener?.onItemRangeInserted(this, index, elements.size)
        }
        return added
    }

    override fun clear() {
        val oldSize = size
        super.clear()
        if (oldSize != 0) {
            onListChangedListener?.onItemRangeRemoved(this, 0, oldSize)
        }
    }

    override fun remove(element: T): Boolean {
        val index = indexOf(element)
        if (index >= 0) {
            removeAt(index)
            return true
        } else {
            return false
        }
    }

    override fun removeAt(index: Int): T {
        val element = super.removeAt(index)
        onListChangedListener?.onItemRangeRemoved(this, index, 1)
        return element
    }

    override fun removeRange(fromIndex: Int, toIndex: Int) {
        super.removeRange(fromIndex, toIndex)
        onListChangedListener?.onItemRangeRemoved(this, fromIndex, toIndex - fromIndex)
    }

    override fun set(index: Int, element: T): T {
        val oldValue = super.set(index, element)
        onListChangedListener?.onItemRangeChanged(this, index, 1)
        return oldValue
    }

    fun setNewList(elements: Collection<T>): Boolean {
        super.clear()
        val added = super.addAll(elements)
        if (added) {
            onListChangedListener?.onChanged(this)
        }
        return added
    }

    fun move(fromPosition: Int, toPosition: Int) {
        val fromElement = get(fromPosition)
        val toElement = get(toPosition)
        super.set(fromPosition, toElement)
        super.set(toPosition, fromElement)
        onListChangedListener?.onItemMoved(this, fromPosition, toPosition)
    }

    fun setOnListChangedListener(listener: OnListChangedListener<ObservableArrayList<T>>) {
        onListChangedListener = listener
    }

    interface OnListChangedListener<T : ObservableArrayList<*>> {

        /**
         * Called whenever a change of unknown type has occurred, such as the entire list being
         * set to new values.
         *
         * @param sender The changing list.
         */
        fun onChanged(sender: T)

        /**
         * Called whenever one or more items in the list have changed.
         * @param sender The changing list.
         * @param positionStart The starting index that has changed.
         * @param itemCount The number of items that have changed.
         */
        fun onItemRangeChanged(sender: T, positionStart: Int, itemCount: Int)

        /**
         * Called whenever items have been inserted into the list.
         * @param sender The changing list.
         * @param positionStart The insertion index
         * @param itemCount The number of items that have been inserted
         */
        fun onItemRangeInserted(sender: T, positionStart: Int, itemCount: Int)

        /**
         * Called whenever items in the list have been moved.
         * @param sender The changing list.
         * @param fromPosition The position from which the items were moved
         * @param toPosition The destination position of the items
         */
        fun onItemMoved(sender: T, fromPosition: Int, toPosition: Int)

        /**
         * Called whenever items in the list have been deleted.
         * @param sender The changing list.
         * @param positionStart The starting index of the deleted items.
         * @param itemCount The number of items removed.
         */
        fun onItemRangeRemoved(sender: T, positionStart: Int, itemCount: Int)
    }
}